diff --git a/bugzoo/core/coverage.py b/bugzoo/core/coverage.py index 84640424c..a715d3579 100644 --- a/bugzoo/core/coverage.py +++ b/bugzoo/core/coverage.py @@ -98,10 +98,10 @@ class TestSuiteCoverage(object): @staticmethod def from_dict(d: dict) -> 'TestSuiteCoverage': coverage_by_test = {} - for test_coverage_dict in d: + for test_coverage_dict in d.values(): test_coverage = TestCoverage.from_dict(test_coverage_dict) coverage_by_test[test_coverage.test] = test_coverage - return ProjectCoverageMap(coverage_by_test) + return TestSuiteCoverage(coverage_by_test) @staticmethod def from_file(fn: str) -> 'TestSuiteCoverage': diff --git a/bugzoo/mgr/bug.py b/bugzoo/mgr/bug.py index e988201ba..9db404794 100644 --- a/bugzoo/mgr/bug.py +++ b/bugzoo/mgr/bug.py @@ -1,4 +1,5 @@ -from typing import Iterator +from typing import Iterator, Optional, List +import os import docker import textwrap @@ -185,10 +186,21 @@ def validate(self, bug: Bug, verbose: bool = True) -> bool: return validated - def coverage(self, bug: Bug) -> TestSuiteCoverage: + def coverage(self, + bug: Bug, + files_to_instrument: Optional[List[str]] = None + ) -> TestSuiteCoverage: """ Provides coverage information for each test within the test suite for the program associated with this bug. + + Parameters: + bug: the bug for which to compute coverage. + files_to_instrument: an optional list of files that should be + instrumented before generating the coverage report. + + Returns: + a test suite coverage report for the given bug. """ # determine the location of the coverage map on disk fn = os.path.join(self.__installation.coverage_path, @@ -201,9 +213,12 @@ def coverage(self, bug: Bug) -> TestSuiteCoverage: # if we don't have coverage information, compute it try: mgr_ctr = self.__installation.containers + mgr_cov = self.__installation.coverage container = None container = mgr_ctr.provision(bug) - coverage = mgr_ctr.coverage(container) + coverage = mgr_cov.coverage(container, + bug.tests, + files_to_instrument=files_to_instrument) # save to disk with open(fn, 'w') as f: diff --git a/bugzoo/mgr/container.py b/bugzoo/mgr/container.py index ed49ac231..5e9b534cc 100644 --- a/bugzoo/mgr/container.py +++ b/bugzoo/mgr/container.py @@ -153,6 +153,20 @@ def provision(self, self.__containers[uid] = container return container + def mktemp(self, + container: Container + ) -> str: + """ + Creates a named temporary file within a given container. + + Returns: + the absolute path to the created temporary file. + """ + response = self.command(container, "mktemp") + assert response.code == 0, "failed to create temporary file" + fn = response.output.strip() + return fn + def ip_address(self, container: Container, raise_error: bool = False diff --git a/bugzoo/mgr/coverage.py b/bugzoo/mgr/coverage.py index e73476987..a826f8844 100644 --- a/bugzoo/mgr/coverage.py +++ b/bugzoo/mgr/coverage.py @@ -56,8 +56,7 @@ def _from_gcovr_xml_string(self, dir_source = container.bug.source_dir # TODO port # getting a list of all files in source directory to later use for resolving path resp = mgr_ctr.command(container, "find {} -type f".format(dir_source)) - all_files = resp.output.split('\n') - + all_files = [fn.strip() for fn in resp.output.split('\n')] def has_file(fn_rel: str) -> bool: fn_abs = os.path.join(dir_source, fn_rel) @@ -233,7 +232,7 @@ def extract(self, """ logger = self.__logger.getChild(container.id) mgr_ctr = self.__installation.containers - logger.debug("Starting to extract coverage info") + logger.debug("Extracting coverage information") if not instrumented_files: instrumented_files = set() @@ -243,17 +242,29 @@ def extract(self, dir_source = container.bug.source_dir # TODO port t_start = timer() logger.debug("Running gcovr.") + fn_temp_ctr = mgr_ctr.mktemp(container) + cmd = 'gcovr -o "{}" -x -d -r .'.format(fn_temp_ctr) response = mgr_ctr.command(container, - 'gcovr -x -d -r .', - context=dir_source) - logger.debug("gcovr returned. Seconds passed: %.2f", timer() - t_start) - assert response.code == 0 - response = response.output + cmd, + context=dir_source, + verbose=True) + logger.debug("Finished running gcovr (took %.2f seconds).", timer() - t_start) + assert response.code == 0, "failed to run gcovr" + + # copy the contents of the temporary file to the host machine + (_, fn_temp_host) = tempfile.mkstemp(suffix='.bugzoo') + try: + mgr_ctr.copy_from(container, fn_temp_ctr, fn_temp_host) + with open(fn_temp_host, 'r') as fh: + report = fh.read() + finally: + os.remove(fn_temp_host) t_start = timer() - logger.debug("Parsing gcovr xml.") - res = self._from_gcovr_xml_string(response, - instrumented_files, - container) - logger.debug("Finished parsing gcovr xml. Seconds passed: %.2f", timer() - t_start) + logger.debug("Parsing gcovr XML report.") + res = self._from_gcovr_xml_string(report, + instrumented_files, + container) + logger.debug("Finished parsing gcovr XML report (took %.2f seconds).", timer() - t_start) + logger.debug("Finished extracting coverage information") return res diff --git a/bugzoo/version.py b/bugzoo/version.py index e7c12d285..f593cd5bd 100644 --- a/bugzoo/version.py +++ b/bugzoo/version.py @@ -1 +1 @@ -__version__ = '2.0.3' +__version__ = '2.0.4'