1616import logging
1717import json
1818import os
19+ from datetime import datetime
1920from typing import Optional , Set , Union
2021from pathlib import Path
2122from fnmatch import fnmatch
23+ from math import log10 , ceil
2224
2325from ..database import PickledDataBase , DataBase
2426from .. import ostools
3537from ..builtins import Builtins
3638from ..vhdl_standard import VHDL , VHDLStandard
3739from ..test .bench_list import TestBenchList
38- from ..test .report import TestReport
40+ from ..test .report import TestReport , get_parsed_time
3941from ..test .runner import TestRunner
4042
4143from .common import LOGGER , TEST_OUTPUT_PATH , select_vhdl_standard , check_not_empty
@@ -749,7 +751,8 @@ def _main(self, post_run):
749751 if self ._args .compile :
750752 return self ._main_compile_only ()
751753
752- all_ok = self ._main_run (post_run )
754+ all_ok = self ._main_run (post_run , self ._args .repeat )
755+
753756 return all_ok
754757
755758 def _create_simulator_if (self ):
@@ -770,7 +773,47 @@ def _create_simulator_if(self):
770773
771774 return self ._simulator_class .from_args (args = self ._args , output_path = self ._simulator_output_path )
772775
773- def _main_run (self , post_run ):
776+ def _print_repetition_report (self , iteration_status , iteration_duration , total_duration ):
777+ n_iterations = len (iteration_status )
778+ n_passed = sum (iteration_status )
779+ n_failed = n_iterations - n_passed
780+
781+ parsed_duration = [get_parsed_time (duration ) for duration in iteration_duration ]
782+ max_parsed_duration = max (len (duration ) for duration in parsed_duration )
783+
784+ first_iteration_name = "Initial test suite"
785+ max_name_length = max (len (first_iteration_name ), len ("Repetition " ) + 1 + int (ceil (log10 (n_iterations - 1 ))))
786+ max_length = 8 + max_name_length + max_parsed_duration
787+ header_prefix = "==== Repetitions Summary "
788+
789+ self ._printer .write (f"\n { header_prefix } " )
790+ self ._printer .write ("=" * (max_length - len (header_prefix )) + "\n " )
791+
792+ for iteration , (status , duration ) in enumerate (zip (iteration_status , iteration_duration )):
793+ iteration_name = f"Repetition { iteration } " if iteration > 0 else first_iteration_name
794+ if not status :
795+ self ._printer .write ("fail" , fg = "ri" )
796+ else :
797+ self ._printer .write ("pass" , fg = "gi" )
798+ self ._printer .write (f" { iteration_name } " )
799+ self ._printer .write (" " * (max_name_length - len (iteration_name )))
800+ self ._printer .write (f"({ get_parsed_time (duration )} )\n " )
801+
802+ self ._printer .write ("=" * max_length + "\n " )
803+ self ._printer .write ("pass" , fg = "gi" )
804+ self ._printer .write (f" { n_passed } of { n_iterations } \n " )
805+ if n_failed > 0 :
806+ self ._printer .write ("fail" , fg = "ri" )
807+ self ._printer .write (f" { n_failed } of { n_iterations } \n " )
808+ self ._printer .write ("=" * max_length + "\n " )
809+ self ._printer .write (f"Total time was { get_parsed_time (total_duration )} \n " )
810+ self ._printer .write ("=" * max_length + "\n " )
811+ if n_failed > 0 :
812+ self ._printer .write ("Some failed!\n " , fg = "ri" )
813+ else :
814+ self ._printer .write ("All passed!\n " , fg = "gi" )
815+
816+ def _main_run (self , post_run , iteration_limit ):
774817 """
775818 Main with running tests
776819 """
@@ -779,30 +822,60 @@ def _main_run(self, post_run):
779822 self ._compile (simulator_if )
780823 print ()
781824
825+ iteration = 1
826+ ctrl_c = False
782827 start_time = ostools .get_time ()
783- report = TestReport (printer = self ._printer )
828+ iteration_status = []
829+ iteration_duration = []
784830
785- try :
786- self ._run_test (test_list , report )
787- except KeyboardInterrupt :
788- print ()
789- LOGGER .debug ("_main: Caught Ctrl-C shutting down" )
790- finally :
791- del test_list
831+ def keep_running ():
832+ if ctrl_c :
833+ return False
834+
835+ if isinstance (iteration_limit , int ):
836+ return iteration <= iteration_limit
837+
838+ return ostools .get_time () < start_time + iteration_limit .total_seconds ()
792839
793- report .set_real_total_time (ostools .get_time () - start_time )
794- report .print_str ()
840+ while keep_running ():
841+ iteration_start_time = ostools .get_time ()
842+ if iteration > 1 :
843+ now = datetime .now ().strftime ("%H:%M:%S" )
844+ print (f"\n ({ now } ) Starting repetition { iteration - 1 } \n " )
795845
796- if post_run is not None :
797- post_run (results = Results (self ._output_path , simulator_if , report ))
846+ report = TestReport (printer = self ._printer )
798847
848+ try :
849+ self ._run_test (test_list , report , iteration )
850+ except KeyboardInterrupt :
851+ ctrl_c = True
852+ print ()
853+ LOGGER .debug ("_main: Caught Ctrl-C shutting down" )
854+ finally :
855+ if sys .exc_info ()[0 ] is not None :
856+ del test_list
857+
858+ iteration_duration .append (ostools .get_time () - iteration_start_time )
859+ report .set_real_total_time (iteration_duration [- 1 ])
860+ report .print_str ()
861+
862+ if post_run is not None :
863+ post_run (results = Results (self ._output_path , simulator_if , report ))
864+
865+ iteration_status .append (report .all_ok ())
866+ iteration += 1
867+
868+ if iteration > 1 :
869+ self ._print_repetition_report (iteration_status , iteration_duration , ostools .get_time () - start_time )
870+
871+ del test_list
799872 del simulator_if
800873
801874 if self ._args .xunit_xml is not None :
802875 xml = report .to_junit_xml_str (self ._args .xunit_xml_format )
803876 ostools .write_file (self ._args .xunit_xml , xml )
804877
805- return report . all_ok ( )
878+ return sum ( iteration_status ) == len ( iteration_status )
806879
807880 def _main_list_only (self ):
808881 """
@@ -938,7 +1011,7 @@ def _get_testbench_files(self, simulator_if: Union[None, SimulatorInterface]):
9381011 for file_name in tb_file_names
9391012 ]
9401013
941- def _run_test (self , test_cases , report ):
1014+ def _run_test (self , test_cases , report , iteration ):
9421015 """
9431016 Run the test suites and return the report
9441017 """
@@ -950,9 +1023,14 @@ def _run_test(self, test_cases, report):
9501023 else :
9511024 verbosity = TestRunner .VERBOSITY_NORMAL
9521025
1026+ full_test_output_path = Path (self ._output_path ) / TEST_OUTPUT_PATH
1027+ if iteration > 1 :
1028+ full_test_output_path /= f"rep_{ iteration - 1 } "
1029+ full_test_output_path = str (full_test_output_path )
1030+
9531031 runner = TestRunner (
9541032 report ,
955- str ( Path ( self . _output_path ) / TEST_OUTPUT_PATH ) ,
1033+ full_test_output_path ,
9561034 verbosity = verbosity ,
9571035 num_threads = self ._args .num_threads ,
9581036 fail_fast = self ._args .fail_fast ,
0 commit comments