@@ -351,6 +351,9 @@ def __init__(
351351 self ._data = []
352352 self ._reporter = rep .Reporter (os .path .join (os .getcwd (), "unitTests-{}.log" .format (tool )))
353353
354+ # List to store tested packages, used for coverage report
355+ self ._packages = []
356+
354357 # By default, include export of FMUs.
355358 self ._include_fmu_test = True
356359
@@ -589,6 +592,7 @@ def getModelicaCommand(self):
589592 elif self ._modelica_tool != 'dymola' :
590593 return 'jm_ipython.sh'
591594 else :
595+ return "C://Program Files//Dymola 2023x//bin64//Dymola"
592596 return self ._modelica_tool
593597
594598 def isExecutable (self , program ):
@@ -771,11 +775,12 @@ def setSinglePackage(self, packageName):
771775
772776 # Set data dictionary as it may have been generated earlier for the whole library.
773777 self ._data = []
774-
778+ self . _packages = []
775779 for pac in packages :
776780 pacSep = pac .find ('.' )
777781 pacPat = pac [pacSep + 1 :]
778782 pacPat = pacPat .replace ('.' , os .sep )
783+ self ._packages .append (pacPat )
779784 rooPat = os .path .join (self ._libHome , 'Resources' , 'Scripts' , 'Dymola' , pacPat )
780785 # Verify that the directory indeed exists
781786 if not os .path .isdir (rooPat ):
@@ -4288,3 +4293,104 @@ def _model_from_mo(self, mo_file):
42884293 model = '.' .join (splt [root :])
42894294 # remove the '.mo' at the end
42904295 return model [:- 3 ]
4296+
4297+ def getCoverage (self ):
4298+ """
4299+ Analyse how many examples are tested.
4300+ If ``setSinglePackage`` is called before this function,
4301+ only packages set will be included. Else, the whole library
4302+ will be checked.
4303+
4304+ Returns:
4305+ - The coverage rate in percent as float
4306+ - The number of examples tested as int
4307+ - The total number of examples as int
4308+ - The list of models not tested as List[str]
4309+ - The list of packages included in the analysis as List[str]
4310+ """
4311+ # first lines copy and paste from run function
4312+ if self .get_number_of_tests () == 0 :
4313+ self .setDataDictionary (self ._rootPackage )
4314+
4315+ # Remove all data that do not require a simulation or an FMU export.
4316+ # Otherwise, some processes may have no simulation to run and then
4317+ # the json output file would have an invalid syntax
4318+
4319+ # to not interact with other code here, we use the temp_data list
4320+
4321+ temp_data = [
4322+ element for element in self ._data [:]
4323+ if element ['mustSimulate' ] or element ['mustExportFMU' ]
4324+ ]
4325+
4326+ # now we got clean _data to compare
4327+ # next step get all examples in the package (whether whole library or
4328+ # single package)
4329+ if self ._packages :
4330+ packages = self ._packages
4331+ else :
4332+ packages = list (dict .fromkeys (
4333+ [pac ['ScriptFile' ].split (os .sep )[0 ] for pac in self ._data ])
4334+ )
4335+
4336+ all_examples = []
4337+ for package in packages :
4338+ package_path = os .path .join (self ._libHome , package )
4339+ for dirpath , dirnames , filenames in os .walk (package_path ):
4340+ for filename in filenames :
4341+ if any (
4342+ xs in filename for xs in ['Examples' , 'Validation' ]
4343+ ) and not filename .endswith (('package.mo' , '.order' )):
4344+ all_examples .append (os .path .abspath (
4345+ os .path .join (dirpath , filename ))
4346+ )
4347+
4348+ coverage = round (len (temp_data ) / len (all_examples ), 2 ) * 100
4349+
4350+ tested_model_names = [
4351+ nam ['ScriptFile' ].split (os .sep )[- 1 ][:- 1 ] for nam in temp_data
4352+ ]
4353+
4354+ missing_examples = [
4355+ i for i in all_examples if not any (
4356+ xs in i for xs in tested_model_names )
4357+ ]
4358+
4359+ n_tested_examples = len (temp_data )
4360+ n_examples = len (all_examples )
4361+ return coverage , n_tested_examples , n_examples , missing_examples , packages
4362+
4363+ def printCoverage (
4364+ self ,
4365+ coverage : float ,
4366+ n_tested_examples : int ,
4367+ n_examples : int ,
4368+ missing_examples : list ,
4369+ packages : list ,
4370+ printer : callable = None
4371+ ) -> None :
4372+ """
4373+ Print the output of getCoverage to inform about
4374+ coverage rate and missing models.
4375+ The default printer is the ``reporter.writeOutput``.
4376+ If another printing method is required, e.g. ``print`` or
4377+ ``logging.info``, it may be passed via the ``printer`` argument.
4378+ """
4379+ if printer is None :
4380+ printer = self ._reporter .writeOutput
4381+ printer ('***\n \n Model Coverage: ' , str (int (coverage )) + '%' )
4382+ printer (
4383+ '***\n \n You are testing : ' ,
4384+ n_tested_examples ,
4385+ ' out of ' ,
4386+ n_examples ,
4387+ 'total examples in '
4388+ )
4389+ for package in packages :
4390+ printer (package )
4391+ printer ('\n ' )
4392+
4393+ if missing_examples :
4394+ print ('***\n \n The following examples are not tested\n ' )
4395+ for i in missing_examples :
4396+ print (i .split (self ._libHome )[1 ])
0 commit comments