-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement code coverage for python and change provider to codecov (#326)
* fix(documentation) remove space between badges * feature(pytests) add steadystate testcases * feature(coverage) create merged report for cpp and py and change to codecov * fix(codecov) remove codecov yaml * fix(codecov) fix lcov command * feature(codecov) simplify report upload * fix(codecov) fix python coverage * doc(codecov) add documentation to testCoverage.py + bugfixes * fix(ci) add python coverage package in travis
- Loading branch information
1 parent
331ad23
commit 17c07e8
Showing
9 changed files
with
212 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -187,3 +187,6 @@ python/.idea/workspace.xml | |
python/testSpeedy.py | ||
|
||
python/test.txt | ||
|
||
tests/test/* | ||
coverage_py.xml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#!/usr/bin/env python3 | ||
|
||
""" | ||
Generate coverage reports for the testModels and testSBML scripts | ||
exported format is cobertura xml | ||
""" | ||
|
||
import coverage | ||
import unittest | ||
import os | ||
import sys | ||
import amici | ||
|
||
import testModels | ||
import testSBML | ||
|
||
# only consider amici module and ignore the swig generated amici.py | ||
cov = coverage.Coverage(source=['amici'],omit=['*/amici.py']) | ||
|
||
# ignore code blocks containing import statements | ||
cov.exclude('import') | ||
cov.start() | ||
|
||
# build the testSuite from testModels and testSBML | ||
suite = unittest.TestSuite() | ||
suite.addTest(testModels.TestAmiciPregeneratedModel()) | ||
suite.addTest(testSBML.TestAmiciSBMLModel()) | ||
testRunner = unittest.TextTestRunner(verbosity=0) | ||
testRunner.run(suite) | ||
|
||
cov.stop() | ||
cov.xml_report(outfile='coverage_py.xml') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,117 +1,53 @@ | ||
#!/usr/bin/env python3 | ||
"""Run SBML Test Suite and verify simulation results [https://github.com/sbmlteam/sbml-test-suite/releases]""" | ||
import traceback | ||
import os | ||
|
||
import sys | ||
import h5py | ||
import amici | ||
import unittest | ||
import importlib | ||
import os | ||
import re | ||
import numpy as np | ||
import amici | ||
|
||
# directory with sbml semantic test cases | ||
test_path = os.path.join(os.path.dirname(__file__), 'sbml-test-suite', 'cases', 'semantic') | ||
class TestAmiciSBMLModel(unittest.TestCase): | ||
''' | ||
TestCase class for tests that were pregenerated using the the matlab code generation routines and cmake | ||
build routines | ||
NOTE: requires having run scripts/buildTests.sh before to build the python modules for the test models | ||
''' | ||
|
||
def runTest(testId, logfile): | ||
try: | ||
current_test_path = os.path.join(test_path, testId) | ||
expectedResultsFile = os.path.join(os.path.dirname(__file__), 'cpputest','expectedResults.h5') | ||
|
||
# results | ||
resultsFile = os.path.join(current_test_path, testId + '-results.csv') | ||
results = np.genfromtxt(resultsFile, delimiter=',') | ||
def runTest(self): | ||
''' | ||
test runner routine that loads data expectedResults.h5 hdf file and runs individual models/settings | ||
as subTests | ||
''' | ||
|
||
# model | ||
sbmlFile = findModelFile(current_test_path, testId) | ||
sbmlFile = os.path.join(os.path.dirname(__file__), '..', 'python', 'examples', 'example_steadystate', 'model_steadystate_scaled.sbml') | ||
sbmlImporter = amici.SbmlImporter(sbmlFile) | ||
sbml = sbmlImporter.sbml | ||
|
||
wrapper = amici.SbmlImporter(sbmlFile) | ||
|
||
modelDir = os.path.join(os.path.dirname(__file__),'SBMLTestModels',testId) | ||
if not os.path.exists(modelDir): | ||
os.makedirs(modelDir) | ||
wrapper.sbml2amici('SBMLTest' + testId, output_dir=modelDir) | ||
|
||
# settings | ||
settings = readSettingsFile(current_test_path, testId) | ||
ts = np.linspace(float(settings['start']), float(settings['start']) + float(settings['duration']), | ||
int(settings['steps']) + 1) | ||
atol = float(settings['absolute']) | ||
rtol = float(settings['relative']) | ||
observables = amici.assignmentRules2observables(sbml, filter=lambda variableId: | ||
variableId.startswith('observable_') and not variableId.endswith('_sigma')) | ||
|
||
sbmlImporter.sbml2amici('test', 'test', | ||
observables=observables, | ||
constantParameters=['k4'], | ||
sigmas={'observable_x1withsigma': 'observable_x1withsigma_sigma'}) | ||
|
||
sys.path.insert(0, wrapper.modelPath) | ||
mod = importlib.import_module(wrapper.modelName) | ||
sys.path.insert(0, 'test') | ||
import test as modelModule | ||
|
||
model = mod.getModel() | ||
model.setTimepoints(mod.amici.DoubleVector(ts)) | ||
model = modelModule.getModel() | ||
model.setTimepoints(amici.DoubleVector(np.linspace(0, 60, 60))) | ||
solver = model.getSolver() | ||
solver.setMaxSteps(int(1e6)) | ||
solver.setRelativeTolerance(rtol / 1000.0) | ||
solver.setAbsoluteTolerance(atol / 1000.0) | ||
rdata = amici.runAmiciSimulation(model, solver) | ||
amountSpecies = settings['amount'].replace(' ', '').replace('\n', '').split(',') | ||
simulated_x = rdata['x'] | ||
test_x = results[1:, [1+ wrapper.speciesIndex[variable] for variable in settings['variables'].replace(' ', '').replace('\n', '').split(',') if variable in wrapper.speciesIndex.keys() ] ] | ||
|
||
for species in amountSpecies: | ||
if not species == '': | ||
volume = wrapper.speciesCompartment[wrapper.speciesIndex[species]].subs(wrapper.compartmentSymbols, | ||
wrapper.compartmentVolume) | ||
simulated_x[:, wrapper.speciesIndex[species]] = simulated_x[:, wrapper.speciesIndex[species]] * volume | ||
pass | ||
|
||
adev = abs(simulated_x - test_x) | ||
adev[np.isnan(adev)] = True | ||
rdev = abs((simulated_x - test_x) / test_x) | ||
rdev[np.isnan(rdev)] = True | ||
if (not np.all(np.logical_or(adev < atol, rdev < rtol))): | ||
if (not np.all(adev < atol)): | ||
raise Exception('Absolute tolerance violated') | ||
|
||
if (not np.all(rdev < rtol)): | ||
raise Exception('Relative tolerance violated') | ||
|
||
except amici.SBMLException as err: | ||
print("Did not run test " + testId + ": {0}".format(err)) | ||
pass | ||
|
||
except Exception as err: | ||
str = "Failed test " + testId + ": {0}".format(err) | ||
traceback.print_exc(10) | ||
logfile.write(str + '\n') | ||
return | ||
|
||
def findModelFile(current_test_path, testId): | ||
"""Find model file for the given test (guess filename extension)""" | ||
sbmlFile = os.path.join(current_test_path, testId + '-sbml-l3v2.xml') | ||
|
||
# fallback l3v1 | ||
if not os.path.isfile(sbmlFile): | ||
sbmlFile = os.path.join(current_test_path, testId + '-sbml-l3v1.xml') | ||
|
||
# fallback l2v5 | ||
if not os.path.isfile(sbmlFile): | ||
sbmlFile = os.path.join(current_test_path, testId + '-sbml-l2v5.xml') | ||
|
||
return sbmlFile | ||
|
||
def readSettingsFile(current_test_path, testId): | ||
"""Read settings for the given test""" | ||
settingsFile = os.path.join(current_test_path, testId + '-settings.txt') | ||
settings = {} | ||
with open(settingsFile) as f: | ||
for line in f: | ||
if not line == '\n': | ||
(key, val) = line.split(':') | ||
settings[key] = val | ||
return settings | ||
|
||
|
||
def getTestStr(testId): | ||
testStr = str(testId) | ||
testStr = '0'*(5-len(testStr)) + testStr | ||
return testStr | ||
|
||
def main(): | ||
for testId in range(1,1781): | ||
with open("test.txt", "w") as logfile: | ||
runTest(getTestStr(testId), logfile) | ||
|
||
if __name__ == '__main__': | ||
main() | ||
suite = unittest.TestSuite() | ||
suite.addTest(TestAmiciPregeneratedModel()) | ||
unittest.main() | ||
|
Oops, something went wrong.