Skip to content

Commit 7549d6e

Browse files
committed
add coverage script from Mans with changes based on review in lbl-srg#315 lbl-srg#243
1 parent c4cfa61 commit 7549d6e

File tree

1 file changed

+107
-1
lines changed

1 file changed

+107
-1
lines changed

buildingspy/development/regressiontest.py

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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\nModel Coverage: ', str(int(coverage)) + '%')
4382+
printer(
4383+
'***\n\nYou 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\nThe following examples are not tested\n')
4395+
for i in missing_examples:
4396+
print(i.split(self._libHome)[1])

0 commit comments

Comments
 (0)