@@ -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,114 @@ 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+         Example: 
4312+             >>> from buildingspy.development.regressiontest import Tester 
4313+             >>> import os 
4314+             >>> ut = Tester(tool='dymola') 
4315+             >>> myMoLib = os.path.join("buildingspy", "tests", "MyModelicaLibrary") 
4316+             >>> ut.setLibraryRoot(myMoLib) 
4317+             >>> ut.setSinglePackage('Examples') 
4318+             >>> coverage_result = ut.getCoverage() 
4319+         """ 
4320+         # first lines copy and paste from run function 
4321+         if  self .get_number_of_tests () ==  0 :
4322+             self .setDataDictionary (self ._rootPackage )
4323+ 
4324+         # Remove all data that do not require a simulation or an FMU export. 
4325+         # Otherwise, some processes may have no simulation to run and then 
4326+         # the json output file would have an invalid syntax 
4327+ 
4328+         # now we got clean _data to compare 
4329+         # next step get all examples in the package (whether whole library or 
4330+         # single package) 
4331+         if  self ._packages :
4332+             packages  =  self ._packages 
4333+         else :
4334+             packages  =  list (dict .fromkeys (
4335+                 [pac ['ScriptFile' ].split (os .sep )[0 ] for  pac  in  self ._data ])
4336+             )
4337+ 
4338+         all_examples  =  []
4339+         for  package  in  packages :
4340+             package_path  =  os .path .join (self ._libHome , package )
4341+             for  dirpath , dirnames , filenames  in  os .walk (package_path ):
4342+                 for  filename  in  filenames :
4343+                     filepath  =  os .path .abspath (os .path .join (dirpath , filename ))
4344+                     if  any (
4345+                             xs  in  filepath  for  xs  in  ['Examples' , 'Validation' ]
4346+                     ) and  not  filepath .endswith (('package.mo' , '.order' )):
4347+                         all_examples .append (filepath )
4348+ 
4349+         n_tested_examples  =  len (temp_data )
4350+         n_examples  =  len (all_examples )
4351+         if  n_examples  >  0 :
4352+             coverage  =  round (n_tested_examples  /  n_examples , 2 ) *  100 
4353+         else :
4354+             coverage  =  100 
4355+ 
4356+         tested_model_names  =  [
4357+             nam ['ScriptFile' ].split (os .sep )[- 1 ][:- 1 ] for  nam  in  self ._data 
4358+         ]
4359+ 
4360+         missing_examples  =  [
4361+             i  for  i  in  all_examples  if  not  any (
4362+                 xs  in  i  for  xs  in  tested_model_names )
4363+         ]
4364+ 
4365+         return  coverage , n_tested_examples , n_examples , missing_examples , packages 
4366+ 
4367+     def  printCoverage (
4368+             self ,
4369+             coverage : float ,
4370+             n_tested_examples : int ,
4371+             n_examples : int ,
4372+             missing_examples : list ,
4373+             packages : list ,
4374+             printer : callable  =  None 
4375+     ) ->  None :
4376+         """ 
4377+         Print the output of getCoverage to inform about 
4378+         coverage rate and missing models. 
4379+         The default printer is the ``reporter.writeOutput``. 
4380+         If another printing method is required, e.g. ``print`` or 
4381+         ``logging.info``, it may be passed via the ``printer`` argument. 
4382+ 
4383+         Example: 
4384+             >>> from buildingspy.development.regressiontest import Tester 
4385+             >>> import os 
4386+             >>> ut = Tester(tool='dymola') 
4387+             >>> myMoLib = os.path.join("buildingspy", "tests", "MyModelicaLibrary") 
4388+             >>> ut.setLibraryRoot(myMoLib) 
4389+             >>> ut.setSinglePackage('Examples') 
4390+             >>> coverage_result = ut.getCoverage() 
4391+             >>> ut.printCoverage(*coverage_result, printer=print) 
4392+         """ 
4393+         if  printer  is  None :
4394+             printer  =  self ._reporter .writeOutput 
4395+         printer (f'***\n Model Coverage: { int (coverage )}  )
4396+         printer (
4397+             f'***\n You are testing: { n_tested_examples }  
4398+             f'out of { n_examples } { "s"  if  len (packages ) >  1  else  "" }  ,
4399+         )
4400+         for  package  in  packages :
4401+             printer (package )
4402+ 
4403+         if  missing_examples :
4404+             print ('***\n The following examples are not tested\n ' )
4405+             for  i  in  missing_examples :
4406+                 print (i .split (self ._libHome )[1 ])
0 commit comments