@@ -545,213 +545,6 @@ def __str__(self) -> str:
545545 # this is used as a title in the trudag report
546546 return f"function: [{ self ._name } ]\n ({ str (self .path )} )"
547547
548- class ListOfTestCases (BaseReference ):
549-
550- def __init__ (self , test_files : list [str ], recent_result_database : str = "artifacts/MemoryEfficientTestResults.db" , recent_result_table : str = "test_results" ) -> None :
551- self ._test_files = test_files
552- self ._database = recent_result_database
553- self ._table = recent_result_table
554-
555- @staticmethod
556- def compile_string (items : list [str ]) -> str :
557- # input: list of strings representing the structure of TEST_CASE, SECTION etc.,
558- # e.g. items = ["lexer class", "scan", "literal names"]
559- # output: the last item of the list, representing the most recent SECTION,
560- # indented as in the source code
561- # throws error if input is empty
562- if len (items ) == 0 :
563- raise RuntimeError ("Received empty structural list; nonempty list expected." )
564- result = ""
565- for _ in range (1 , len (items )):
566- result += " "
567- if items :
568- result += "* " + items [- 1 ]
569- return result
570-
571- @staticmethod
572- def extract_quotation (s : str ) -> str :
573- # input: string containing at least one quoted substring, e.g. s = "my \"input\""
574- # output: the first quoted substring of the input
575- # throws error if no quoted substring can be found.
576- first = s .find ('"' )
577- if first == - 1 :
578- raise RuntimeError ("Expected quotation mark; none were detected." )
579- second = s .find ('"' , first + 1 )
580- if second == - 1 :
581- raise RuntimeError ("Expected quotation marks; only one was detected." )
582- return s [first + 1 : second ]
583-
584- @staticmethod
585- def remove_and_count_indent (s : str ) -> tuple [int , str ]:
586- # input: string with possibly leading whitespace (space of horizontal tab)
587- # output: the number of leading spaces and the string with leading whitespace removed;
588- # tab counted as four spaces
589- cnt = 0
590- i = 0
591- n = len (s )
592- while i < n and (s [i ] == " " or s [i ] == "\t " ):
593- if s [i ] == " " :
594- cnt += 1
595- elif s [i ] == "\t " :
596- cnt += 4
597- i += 1
598- return (cnt , s [i :])
599-
600- @staticmethod
601- def head_of_list () -> str :
602- return """## List of all unit-tests with test environments
603-
604- This list contains all unit-tests possibly running in this project.
605- These tests are compiled from the source-code, where the individual unit-tests are arranged in TEST_CASEs containing possibly nested SECTIONs.
606- To reflect the structure of the nested sections, nested lists are utilised, where the top-level list represents the list of TEST_CASEs.
607-
608- It should be noted that not all unit-tests in a test-file are executed with every compiler-configuration.
609- """
610-
611- @staticmethod
612- def transform_test_file_to_test_name (test_file : str ) -> str :
613- return "test-" + "-" .join ((test_file .split ('.' )[0 ]).split ('-' )[1 :])
614-
615- @classmethod
616- def type (cls ) -> str :
617- return "list_of_test_cases"
618-
619- def extract_test_structure (self , file_path : Path ) -> str :
620- # input: path to a file potentially containing unit-tests
621- # output: the extracted arrangement of TEST_CASE and SECTION
622- # in the form of nested markdown lists
623-
624- indent = 0 # the indent of the currently read line
625- current_indent = 0 # the indent of the last TEST_CASE or SECTION
626- current_path = [] # the current path
627- lines_out = [] # the collection of lines to be outputted
628-
629- # open file_path as read-only, and process line by line
630- with file_path .open ("r" , encoding = "utf-8" , errors = "replace" ) as source :
631- for line in source :
632- # count and remove leading whitespace
633- indent , trimmed = self .remove_and_count_indent (str (line ))
634-
635- # check whether we have found a TEST_CASE
636- if trimmed .startswith ("TEST_CASE(" ) or trimmed .startswith ("TEST_CASE_TEMPLATE(" ) or trimmed .startswith ("TEST_CASE_TEMPLATE_DEFINE(" ):
637- # remember the current indent
638- current_indent = indent
639- # TEST_CASE is always the head of a new arrangement-structure
640- # remove stored structure
641- current_path .clear ()
642- # extract name of TEST_CASE and append path
643- current_path .append (self .extract_quotation (trimmed ))
644- lines_out .append (self .compile_string (current_path ))
645-
646- # check whether we have found a SECTION
647- if trimmed .startswith ("SECTION(" ):
648- # update path to reflect arrangement of current section
649- while indent <= current_indent and current_path :
650- current_path .pop ()
651- current_indent -= 4
652- # remember the current indent
653- current_indent = indent
654- # extract name of SECTION and append path
655- current_path .append (self .extract_quotation (trimmed ))
656- lines_out .append (self .compile_string (current_path ))
657-
658- # process extracted lines
659- return ("\n " .join (lines_out ) + "\n " ) if lines_out else ""
660-
661- def extract_recent_test_environments (self ) -> dict :
662- fetched_data = dict ()
663- try :
664- # initialise connection to test result database
665- connector = sqlite3 .connect (self ._database )
666- cursor = connector .cursor ()
667- # verify that the expected table does exist
668- cursor .execute ("SELECT name FROM sqlite_master WHERE type='table' AND name = ?;" ,(self ._table ,))
669- if cursor .fetchone () is None :
670- raise RuntimeError (f"Fatal Error: Could not find table { self ._table } in database { self ._database } ." )
671- except sqlite3 .Error as e :
672- raise RuntimeError (f"Fatal Error accessing database { self ._database } : { e } " )
673- # get all test-files from recent test executions
674- command = f"SELECT name FROM { self ._table } ;"
675- cursor .execute (command )
676- raw_cases = cursor .fetchall ()
677- cases = set ([raw_case [0 ] for raw_case in raw_cases ])
678- # for each test-file
679- for case in cases :
680- case_data = dict ()
681- # get the test-environments
682- command = f"SELECT compiler, cpp_standard FROM { self ._table } WHERE name = ? and skipped_cases == 0"
683- cursor .execute (command ,(case ,))
684- results = cursor .fetchall ()
685- case_data ["noskip" ] = [{"compiler" :result [0 ], "standard" :result [1 ]} for result in results ]
686- # some test-cases are skipped with certain environments
687- # It is unclear from the log, which cases are skipped;
688- # we leave this to the interested reader
689- command = f"SELECT compiler, cpp_standard, skipped_cases FROM { self ._table } WHERE name = ? and skipped_cases != 0"
690- cursor .execute (command , (case ,))
691- results = cursor .fetchall ()
692- case_data ["skip" ] = [{"compiler" : result [0 ], "standard" : result [1 ], "skipped" : result [2 ]} for result in results ]
693- fetched_data [case ] = case_data
694- return fetched_data
695-
696- def fetch_all_test_data (self , input : list [str ]):
697- # inputs: path(s) to directory potentially containing some test-data
698- extracted_test_data = []
699- recent_test_data = self .extract_recent_test_environments ()
700- for arg in input :
701- p = Path (arg )
702- if p .is_file () and p .suffix == ".cpp" and p .name .startswith ("unit-" ):
703- extracted_test_data .append ((p .name ,self .extract_test_structure (p )))
704- elif p .is_dir ():
705- for entry in p .rglob ("*" ):
706- if entry .is_file () and entry .suffix == ".cpp" and entry .name .startswith ("unit-" ):
707- extracted_test_data .append ((entry .name ,self .extract_test_structure (entry )))
708- extracted_test_data .sort (key = lambda x : x [0 ])
709- result = self .head_of_list ()
710- for test_file , list_of_tests in extracted_test_data :
711- result += f"\n \n ### List of tests in file { test_file } \n \n "
712- result += list_of_tests
713- result += "\n \n "
714- if recent_test_data .get (self .transform_test_file_to_test_name (test_file ), None ) is None :
715- result += "Unfortunately, none of the following tests seems to have been executed. Very strange indeed!\n \n "
716- else :
717- if recent_test_data .get (self .transform_test_file_to_test_name (test_file )).get ("noskip" ,None ) is not None :
718- if len (recent_test_data .get (self .transform_test_file_to_test_name (test_file )).get ("noskip" )) != 0 :
719- result += "\n All tests in this file were run in the following configurations:\n \n "
720- for datum in recent_test_data .get (self .transform_test_file_to_test_name (test_file )).get ("noskip" ):
721- result += "* "
722- result += datum .get ("compiler" ,None )
723- result += " with standard "
724- result += datum .get ("standard" ,None )
725- result += "\n "
726- if recent_test_data .get (self .transform_test_file_to_test_name (test_file )).get ("skip" ,None ) is not None :
727- if len (recent_test_data .get (self .transform_test_file_to_test_name (test_file )).get ("skip" )) != 0 :
728- result += "\n In the following configuration, however, some test-cases were skipped:\n \n "
729- for datum in recent_test_data .get (self .transform_test_file_to_test_name (test_file )).get ("skip" ):
730- result += "* "
731- how_many = datum .get ("skipped" ,None )
732- result += str (how_many )
733- if how_many == 1 :
734- result += " test case was skipped when using "
735- else :
736- result += " test cases were skipped when using "
737- result += datum .get ("compiler" ,None )
738- result += " with standard "
739- result += datum .get ("standard" ,None )
740- result += "\n "
741- return result
742-
743- @property
744- def content (self ) -> bytes :
745- # encoding is necessary since content will be hashed
746- return self .fetch_all_test_data (self ._test_files ).encode ('utf-8' )
747-
748- def as_markdown (self , filepath : None | str = None ) -> str :
749- return self .content .decode ('utf-8' )
750-
751- def __str__ (self ) -> str :
752- # this is used as a title in the trudag report
753- return "List of all unit-tests"
754-
755548from trudag .dotstop .core .reference .references import LocalFileReference as LFR
756549
757550class VerboseFileReference (LFR ):
0 commit comments