@@ -352,6 +352,9 @@ def __init__(
352
352
self ._data = []
353
353
self ._reporter = rep .Reporter (os .path .join (os .getcwd (), "unitTests-{}.log" .format (tool )))
354
354
355
+ # List to store tested packages, used for coverage report
356
+ self ._packages = []
357
+
355
358
# By default, include export of FMUs.
356
359
self ._include_fmu_test = True
357
360
@@ -776,11 +779,12 @@ def setSinglePackage(self, packageName):
776
779
777
780
# Set data dictionary as it may have been generated earlier for the whole library.
778
781
self ._data = []
779
-
782
+ self . _packages = []
780
783
for pac in packages :
781
784
pacSep = pac .find ('.' )
782
785
pacPat = pac [pacSep + 1 :]
783
786
pacPat = pacPat .replace ('.' , os .sep )
787
+ self ._packages .append (pacPat )
784
788
rooPat = os .path .join (self ._libHome , 'Resources' , 'Scripts' , 'Dymola' , pacPat )
785
789
# Verify that the directory indeed exists
786
790
if not os .path .isdir (rooPat ):
@@ -4297,3 +4301,130 @@ def _model_from_mo(self, mo_file):
4297
4301
model = '.' .join (splt [root :])
4298
4302
# remove the '.mo' at the end
4299
4303
return model [:- 3 ]
4304
+
4305
+ def getCoverage (self ):
4306
+ """
4307
+ Analyse how many examples are tested.
4308
+ If ``setSinglePackage`` is called before this function,
4309
+ only packages set will be included. Else, the whole library
4310
+ will be checked.
4311
+
4312
+ Returns:
4313
+ - The coverage rate in percent as float
4314
+ - The number of examples tested as int
4315
+ - The total number of examples as int
4316
+ - The list of models not tested as List[str]
4317
+ - The list of packages included in the analysis as List[str]
4318
+
4319
+ Example:
4320
+ >>> from buildingspy.development.regressiontest import Tester
4321
+ >>> import os
4322
+ >>> ut = Tester(tool='dymola')
4323
+ >>> myMoLib = os.path.join("buildingspy", "tests", "MyModelicaLibrary")
4324
+ >>> ut.setLibraryRoot(myMoLib)
4325
+ >>> ut.setSinglePackage('Examples')
4326
+ Regression tests are only run for the following package:
4327
+ Examples
4328
+ MyModelicaLibrary.Examples.NoSolution: Excluded from simulation. Model excluded from simulation as it has no solution.
4329
+ >>> coverage_result = ut.getCoverage()
4330
+ """
4331
+ # first lines copy and paste from run function
4332
+ if self .get_number_of_tests () == 0 :
4333
+ self .setDataDictionary (self ._rootPackage )
4334
+
4335
+ # Remove all data that do not require a simulation or an FMU export.
4336
+ # Otherwise, some processes may have no simulation to run and then
4337
+ # the json output file would have an invalid syntax
4338
+
4339
+ # now we got clean _data to compare
4340
+ # next step get all examples in the package (whether whole library or
4341
+ # single package)
4342
+ if self ._packages :
4343
+ packages = self ._packages
4344
+ else :
4345
+ packages = list (dict .fromkeys (
4346
+ [pac ['ScriptFile' ].split (os .sep )[0 ] for pac in self ._data ])
4347
+ )
4348
+
4349
+ all_examples = []
4350
+ for package in packages :
4351
+ package_path = os .path .join (self ._libHome , package )
4352
+ for dirpath , dirnames , filenames in os .walk (package_path ):
4353
+ for filename in filenames :
4354
+ filepath = os .path .abspath (os .path .join (dirpath , filename ))
4355
+ if any (
4356
+ xs in filepath for xs in ['Examples' , 'Validation' ]
4357
+ ) and not filepath .endswith (('package.mo' , '.order' )):
4358
+ all_examples .append (filepath )
4359
+
4360
+ n_tested_examples = len (self ._data )
4361
+ n_examples = len (all_examples )
4362
+ if n_examples > 0 :
4363
+ coverage = round (n_tested_examples / n_examples , 2 ) * 100
4364
+ else :
4365
+ coverage = 100
4366
+
4367
+ tested_model_names = [
4368
+ nam ['ScriptFile' ].split (os .sep )[- 1 ][:- 1 ] for nam in self ._data
4369
+ ]
4370
+
4371
+ missing_examples = [
4372
+ i for i in all_examples if not any (
4373
+ xs in i for xs in tested_model_names )
4374
+ ]
4375
+
4376
+ return coverage , n_tested_examples , n_examples , missing_examples , packages
4377
+
4378
+ def printCoverage (
4379
+ self ,
4380
+ coverage : float ,
4381
+ n_tested_examples : int ,
4382
+ n_examples : int ,
4383
+ missing_examples : list ,
4384
+ packages : list ,
4385
+ printer : callable = None
4386
+ ) -> None :
4387
+ """
4388
+ Print the output of getCoverage to inform about
4389
+ coverage rate and missing models.
4390
+ The default printer is the ``reporter.writeOutput``.
4391
+ If another printing method is required, e.g. ``print`` or
4392
+ ``logging.info``, it may be passed via the ``printer`` argument.
4393
+
4394
+ Example:
4395
+ >>> from buildingspy.development.regressiontest import Tester
4396
+ >>> import os
4397
+ >>> ut = Tester(tool='dymola')
4398
+ >>> myMoLib = os.path.join("buildingspy", "tests", "MyModelicaLibrary")
4399
+ >>> ut.setLibraryRoot(myMoLib)
4400
+ >>> ut.setSinglePackage('Examples')
4401
+ Regression tests are only run for the following package:
4402
+ Examples
4403
+ MyModelicaLibrary.Examples.NoSolution: Excluded from simulation. Model excluded from simulation as it has no solution.
4404
+ >>> coverage_result = ut.getCoverage()
4405
+ >>> ut.printCoverage(*coverage_result, printer=print)
4406
+ ***
4407
+ Model Coverage: 88 %
4408
+ ***
4409
+ You are testing: 7 out of 8 examples in package:
4410
+ Examples
4411
+ ***
4412
+ The following examples are not tested
4413
+ <BLANKLINE>
4414
+ /Examples/ParameterEvaluation.mo
4415
+
4416
+ """
4417
+ if printer is None :
4418
+ printer = self ._reporter .writeOutput
4419
+ printer (f'***\n Model Coverage: { int (coverage )} %' )
4420
+ printer (
4421
+ f'***\n You are testing: { n_tested_examples } '
4422
+ f'out of { n_examples } examples in package{ "s" if len (packages ) > 1 else "" } :' ,
4423
+ )
4424
+ for package in packages :
4425
+ printer (package )
4426
+
4427
+ if missing_examples :
4428
+ print ('***\n The following examples are not tested\n ' )
4429
+ for i in missing_examples :
4430
+ print (i .split (self ._libHome )[1 ])
0 commit comments