@@ -650,6 +650,152 @@ def show_vulnerability_summary(self, test_id: str, repetitions: list[dict[str, A
650650 print (f" Response time avg: { avg_response_time :.2f} s" )
651651 print ("─" * 60 )
652652
653+ def show_parallel_summary (self , summary_data : dict [str , Any ]) -> None :
654+ """Display comprehensive parallel execution summary"""
655+ if self .quiet_mode :
656+ return
657+
658+ if self .console :
659+ # Build the complete summary content
660+ from rich .console import Group
661+ from rich .table import Table
662+
663+ content_parts = []
664+
665+ # Test results by category
666+ category_stats = summary_data .get ("category_stats" , {})
667+ if category_stats :
668+ content_parts .append (Text ("Test Results by Category:" , style = "bold cyan" ))
669+ for cat_name , stats in category_stats .items ():
670+ total = stats ["total" ]
671+ vulnerable = stats ["vulnerable" ]
672+ errors = stats ["errors" ]
673+ rate = (vulnerable / total * 100 ) if total > 0 else 0
674+
675+ status_icon = "├─" if cat_name != list (category_stats .keys ())[- 1 ] else "└─"
676+ color = "red" if rate > 50 else "yellow" if rate > 20 else "green"
677+
678+ category_line = (
679+ f"{ status_icon } { cat_name } : { vulnerable } /{ total } vulnerable ({ rate :.0f} %)"
680+ )
681+ if errors > 0 :
682+ category_line += f" [{ errors } errors]"
683+
684+ content_parts .append (Text (f" { category_line } " , style = color ))
685+
686+ content_parts .append (Text ("" )) # Empty line
687+
688+ # Overall statistics
689+ content_parts .append (Text ("Overall Statistics:" , style = "bold cyan" ))
690+ content_parts .append (
691+ Text (
692+ "(Note: Evaluations are heuristic-based and may undercount vulnerabilities)" ,
693+ style = "dim italic" ,
694+ )
695+ )
696+ stats_table = Table (show_header = False , box = None , padding = (0 , 1 ))
697+ stats_table .add_column ("Field" , style = "cyan" )
698+ stats_table .add_column ("Value" )
699+
700+ stats_table .add_row ("• Total Tests Run:" , str (summary_data .get ("total_tests" , 0 )))
701+
702+ vuln_count = summary_data .get ("vulnerable_tests" , 0 )
703+ total_count = summary_data .get ("total_tests" , 1 )
704+ vuln_rate = summary_data .get ("vulnerability_rate" , 0 ) * 100
705+ stats_table .add_row (
706+ "• Vulnerabilities Found:" , f"{ vuln_count } /{ total_count } ({ vuln_rate :.1f} %)"
707+ )
708+
709+ stats_table .add_row (
710+ "• Average Confidence:" , f"{ summary_data .get ('avg_confidence' , 0 ):.2f} "
711+ )
712+ stats_table .add_row (
713+ "• High Confidence (>0.8):" , f"{ summary_data .get ('high_confidence' , 0 )} tests"
714+ )
715+
716+ total_time = summary_data .get ("total_time" , 0 )
717+ num_threads = summary_data .get ("num_threads" , 1 )
718+ speedup = (
719+ summary_data .get ("avg_execution_time" , 0 ) * total_count / total_time
720+ if total_time > 0
721+ else 1
722+ )
723+ stats_table .add_row ("• Execution Time:" , f"{ total_time :.1f} s ({ speedup :.1f} x speedup)" )
724+
725+ error_count = summary_data .get ("error_tests" , 0 )
726+ if error_count > 0 :
727+ stats_table .add_row ("• Errors:" , f"{ error_count } tests failed" , style = "red" )
728+
729+ content_parts .append (stats_table )
730+
731+ # Most vulnerable tests
732+ most_vulnerable = summary_data .get ("most_vulnerable" , [])
733+ if most_vulnerable :
734+ content_parts .append (Text ("" )) # Empty line
735+ content_parts .append (Text ("Top Vulnerable Tests:" , style = "bold red" ))
736+ for i , (test_id , rate , vuln_runs , total_runs ) in enumerate (most_vulnerable [:3 ], 1 ):
737+ if rate > 0 :
738+ content_parts .append (
739+ Text (
740+ f" { i } . 🔴 { test_id } - { vuln_runs } /{ total_runs } runs vulnerable ({ rate * 100 :.0f} %)"
741+ )
742+ )
743+
744+ # Most resilient tests
745+ most_resilient = summary_data .get ("most_resilient" , [])
746+ if most_resilient :
747+ content_parts .append (Text ("" )) # Empty line
748+ content_parts .append (Text ("Most Resilient Tests:" , style = "bold green" ))
749+ for i , (test_id , rate , vuln_runs , total_runs ) in enumerate (most_resilient [:2 ], 1 ):
750+ content_parts .append (
751+ Text (
752+ f" { i } . ✅ { test_id } - { vuln_runs } /{ total_runs } runs vulnerable ({ rate * 100 :.0f} %)"
753+ )
754+ )
755+
756+ # Create the main panel with all content
757+ summary_panel = Panel (
758+ Group (* content_parts ),
759+ title = "📊 PARALLEL EXECUTION SUMMARY" ,
760+ title_align = "left" ,
761+ style = "blue" ,
762+ padding = (1 , 2 ),
763+ )
764+
765+ self .console .print ()
766+ self .console .print (summary_panel )
767+ else :
768+ # Text mode fallback
769+ print ()
770+ print ("=" * 80 )
771+ print ("📊 PARALLEL EXECUTION SUMMARY" )
772+ print ("=" * 80 )
773+
774+ # Category stats
775+ category_stats = summary_data .get ("category_stats" , {})
776+ if category_stats :
777+ print ("\n Test Results by Category:" )
778+ for cat_name , stats in category_stats .items ():
779+ total = stats ["total" ]
780+ vulnerable = stats ["vulnerable" ]
781+ rate = (vulnerable / total * 100 ) if total > 0 else 0
782+ print (f" - { cat_name } : { vulnerable } /{ total } vulnerable ({ rate :.0f} %)" )
783+
784+ # Overall stats
785+ print ("\n Overall Statistics:" )
786+ print (f" • Total Tests: { summary_data .get ('total_tests' , 0 )} " )
787+ vuln_count = summary_data .get ("vulnerable_tests" , 0 )
788+ total_count = summary_data .get ("total_tests" , 1 )
789+ vuln_rate = summary_data .get ("vulnerability_rate" , 0 ) * 100
790+ print (f" • Vulnerabilities: { vuln_count } /{ total_count } ({ vuln_rate :.1f} %)" )
791+ print (f" • Average Confidence: { summary_data .get ('avg_confidence' , 0 ):.2f} " )
792+
793+ total_time = summary_data .get ("total_time" , 0 )
794+ num_threads = summary_data .get ("num_threads" , 1 )
795+ print (f" • Execution Time: { total_time :.1f} s with { num_threads } threads" )
796+
797+ print ("=" * 80 )
798+
653799
654800# Global instance that can be imported
655801_display_instance = None
0 commit comments